package com.triones.biz.infra.service.file.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.triones.biz.infra.controller.enums.SceneEnum;
import com.triones.framework.common.util.file.FileUtils;
import com.triones.framework.file.core.client.FileClient;
import com.triones.framework.file.core.util.FileTypeUtils;
import com.triones.biz.infra.entity.file.FileDO;
import com.triones.biz.infra.entity.file.bo.FileBO;
import com.triones.biz.infra.entity.file.vo.FileVO;
import com.triones.biz.infra.mapper.file.FileMapper;
import com.triones.biz.infra.service.file.FileConfigService;
import com.triones.biz.infra.service.file.FileService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static com.triones.biz.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;

/**
 * 文件 Service 业务层处理
 *
 * @author triones
 * 2023-09-20
 */
@RequiredArgsConstructor
@Service
public class FileServiceImpl implements FileService {

    private final FileMapper fileMapper;

    private final FileConfigService fileConfigService;

    private final RuoYiConfig ruoYiConfig;

    /**
     * 保存文件
     *
     * @param name    文件名称
     * @param path    文件路径
     * @param content 文件内容
     * @return
     */
    @Override
    @SneakyThrows
    public FileVO upload(String name, String path, byte[] content, String scene) {
        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);
        if (StrUtil.isEmpty(path)) {
            path = FileUtils.generatePath(content, name);
        }
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }

        // 上传到文件存储器
        FileClient client = fileConfigService.getMasterFileClient();
        Assert.notNull(client, "客户端(master) 不能为空");
//        path = "/" + ruoYiConfig.getName() + "/" + SceneEnum.convert(scene).getScene() + "/" + path;
        path = ruoYiConfig.getName() + "/" + SceneEnum.convert(scene).getScene() + "/" + path;
        String url = client.upload(content, path, type);

        // 保存到数据库
        FileDO file = new FileDO();
        file.setConfigId(client.getId());
        file.setName(name);
        file.setPath(path);
        file.setUrl(url);
        file.setType(type);
        file.setSize(content.length);
        fileMapper.insert(file);
        return BeanUtil.toBean(file, FileVO.class);
    }

    /**
     * 查询文件
     */
    @Override
//    @Cacheable(cacheNames = CacheNames.FILE, key = "#id")
    @CacheEvict(value = "file", key = "#id")
    public FileVO queryById(Long id) {
        return fileMapper.selectVoById(id);
    }

    /**
     * 查询文件列表
     */
    @Override
    public TableDataInfo<FileVO> queryPageList(FileBO bo, PageQuery pageQuery) {
        LambdaQueryWrapper<FileDO> lqw = buildQueryWrapper(bo);
        Page<FileVO> result = fileMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询文件列表
     */
    @Override
    public List<FileVO> listByIds(Collection<Long> ids) {
        List<FileVO> list = new ArrayList<>();
        for (Long id : ids) {
            FileVO vo = SpringUtils.getAopProxy(this).queryById(id);
            if (ObjectUtil.isNotNull(vo)) {
                list.add(this.matchingUrl(vo));
            }
        }
        return list;
    }

    /**
     * 匹配Url
     */
    private FileVO matchingUrl(FileVO vo) {
        return vo;
    }

    /**
     * 查询文件列表
     */
    @Override
    public List<FileVO> queryList(FileBO bo) {
        LambdaQueryWrapper<FileDO> lqw = buildQueryWrapper(bo);
        return fileMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<FileDO> buildQueryWrapper(FileBO bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<FileDO> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getConfigId() != null, FileDO::getConfigId, bo.getConfigId());
        lqw.like(StringUtils.isNotBlank(bo.getName()), FileDO::getName, bo.getName());
        lqw.eq(StringUtils.isNotBlank(bo.getPath()), FileDO::getPath, bo.getPath());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), FileDO::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getType()), FileDO::getType, bo.getType());
        lqw.eq(bo.getSize() != null, FileDO::getSize, bo.getSize());
        return lqw;
    }

    /**
     * 批量删除文件
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) throws Exception {
        List<FileDO> fileList = new ArrayList<>();
        if (isValid) {
            for (Long id : ids) {
                // 校验存在
                FileDO file = validateFileExists(id);
                fileList.add(file);
            }
        }
        boolean flag = fileMapper.deleteBatchIds(ids) > 0;
        if (flag) {
            for (FileDO file : fileList) {
                // 从文件存储器中删除
                FileClient client = fileConfigService.getFileClient(file.getConfigId());
                Assert.notNull(client, "客户端({}) 不能为空", file.getConfigId());
                client.delete(file.getPath());
            }
        }
        return flag;
    }

    /**
     * 获得文件内容
     *
     * @param configId 配置编号
     * @param path     文件路径
     * @return
     */
    @Override
    public byte[] getFileContent(Long configId, String path) throws Exception {
        FileClient client = fileConfigService.getFileClient(configId);
        Assert.notNull(client, "客户端({}) 不能为空", configId);
        return client.getContent(path);
    }

    private FileDO validateFileExists(Long id) {
        FileDO fileDO = fileMapper.selectById(id);
        if (fileDO == null) {
            throw new ServiceException(FILE_NOT_EXISTS.getMsg());
        }
        return fileDO;
    }
}
